package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestRedisService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class TestRedisServiceImpl implements TestRedisService {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 使用redis 的SetNx 实现分布式锁
     */
    @Override
    public void setRedisBySetNx() {
        //使用UUID 为每一操作线程生成唯一的值
        String uuid = UUID.randomUUID().toString().replace("-", "");
        //使用redis的sentNx设置分布式锁，当得到锁时才能操作redis中key为java0509的这条数据的值
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 5, TimeUnit.SECONDS);
        try {
            if (lock) {
                //获取锁成功
                try {
                    //从redis中获取一个key
                    Integer java0509 = (Integer) redisTemplate.opsForValue().get("java0509");
                    //这个key的value不为空的话,进行i++操作
                    if (java0509 != null) {
                        java0509++;
                        //操作完成以后,将i重新写入redis中去
                        redisTemplate.opsForValue().set("java0509", java0509); }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    /*//判断是不是自己的锁
                    String redisUuid = (String) redisTemplate.opsForValue().get("lock");
                    if (redisUuid.equals(uuid)) {
                        //是自己的锁  释放锁
                        redisTemplate.delete("lock"); }*/
                    //一步完成释放锁: sql-面向过程 lua脚本 .sh
                    //脚本初始化
                    DefaultRedisScript<Long> script = new DefaultRedisScript();
                    //设置lua脚本
                    script.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end");
                    //设置返回类型
                    script.setResultType(Long.class);
                    //执行脚本释放锁
                    redisTemplate.execute(script, Arrays.asList("lock"), uuid);
                }
            }else {
                //获取锁失败
                Thread.sleep(100);
                //重试
                setRedisBySetNx(); }
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 使用redission 中间件实现分布式锁
     */
    @Autowired
    private RedissonClient redissonClient;
    @Override
    public void setRedisByRedission() {
        //获得锁
        RLock lock = redissonClient.getLock("lock");
        try {
            // lock.lock(): 在这里加锁到成功为止
            //使用lock的tryLock方法尝试获得锁，设置等待时间如果到了等待时间未获得锁直接失败
            // lock.tryLock(10, 10, TimeUnit.SECONDS): 指定时间段内一直尝试加锁,除非超时,加锁成功后锁的有效期是多久
            if (lock.tryLock(100,10,TimeUnit.SECONDS)){
                // 获得锁成功加锁
                try {
                    //从redis中获取一个key
                    Integer java0509 = (Integer) redisTemplate.opsForValue().get("java0509");
                    //这个key的value不为空的话,进行i++操作
                    if (java0509 != null) {
                        java0509++;
                        //操作完成以后,将i重新写入redis中去
                        redisTemplate.opsForValue().set("java0509", java0509); }
                }catch (Exception e){
                    e.printStackTrace();
                    System.out.println("加锁成功，数据操作异常");
                }finally {
                    //操作完成释放锁
                    lock.unlock();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("或取锁失败");
        }
    }
}

